﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Cesave.Domain.Core;
using Cesave.Domain.PermisosModule.Entities;
using Cesave.Domain.PermisosModule.Resources;
using Cesave.Infrastructure.CrossCutting.Hashing;

namespace Cesave.Domain.PermisosModule.Contracts.Agentes
{
	/// <summary>
	/// 
	/// </summary>
	public class AgenteService
		: IAgenteService
	{
		#region Private properties

		/// <summary>
		/// 
		/// </summary>
		private readonly IAgenteRepository _agenteRepository;

		#endregion

		#region Constructor

		/// <summary>
		/// 
		/// </summary>
		/// <param name="agenteRepository"></param>
		public AgenteService(IAgenteRepository agenteRepository)
		{
			if (agenteRepository == null)
				throw new ArgumentNullException("agenteRepository", Messages.exception_DependenciesAreNotInitialized);
			_agenteRepository = agenteRepository;
		}

		#endregion

		#region IAgenteService Members

		/// <summary>
		/// 
		/// </summary>
		/// <param name="juntaGuid"></param>
		/// <returns></returns>
		public List<Agente> FindAgentesByJunta(Guid juntaGuid)
		{
			if (juntaGuid == Guid.Empty)
				throw new InvalidOperationException(Messages.exception_InvalidArgument);
			AgentesByJuntaSpecification agentesByJuntaSpecification = new AgentesByJuntaSpecification(juntaGuid);
			return _agenteRepository.FindAgentesByJuntaSpecification(agentesByJuntaSpecification).ToList();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="juntaGuid"></param>
		/// <param name="status"></param>
		/// <returns></returns>
		public List<Agente> FindAgentesByJuntaAndStatus(Guid juntaGuid, bool status = true)
		{
			if (juntaGuid == Guid.Empty)
				throw new InvalidOperationException(Messages.exception_InvalidArgument);
			AgentesByJuntaSpecification agentesByJuntaSpecification = new AgentesByJuntaSpecification(juntaGuid);
			return _agenteRepository.FindAgentesByJuntaAndStatusSpecification(agentesByJuntaSpecification, status).ToList();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="aclGuid"></param>
		/// <returns></returns>
		public List<Agente> FindAgentesByAcl(Guid aclGuid)
		{
			if (aclGuid == Guid.Empty)
				throw new InvalidOperationException(Messages.exception_InvalidArgument);
			AgentesByAclSpecification agentesByAclSpecification = new AgentesByAclSpecification(aclGuid);
			return _agenteRepository.FindAgentesByAclSpecification(agentesByAclSpecification).ToList();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="agenteGuid"></param>
		/// <returns></returns>
		public Agente FindAgenteByGuid(Guid agenteGuid)
		{
			if (agenteGuid == Guid.Empty)
				throw new InvalidOperationException(Messages.exception_InvalidArgument);
			AgenteByGuidSpecification agenteByGuidSpecification = new AgenteByGuidSpecification(agenteGuid);
			return _agenteRepository.FindAgenteByGuidSpecification(agenteByGuidSpecification);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="agente"></param>
		public void AddAgente(Agente agente)
		{
			if (agente == null)
				throw new ArgumentNullException("agente");
			IUnitOfWork unitOfWork = _agenteRepository.StoreContext;
			string saltKey = Guid.NewGuid().ToString();
			string newSalt = SaltAndHash.Hash(saltKey);
			string newHash = SaltAndHash.Hash(agente.PasswordHash, newSalt);
			agente.PasswordHash = newHash;
			agente.PasswordSalt = newSalt;
			_agenteRepository.Add(agente);
			try
			{
				unitOfWork.Commit();
			}
			catch (OptimisticConcurrencyException)
			{
				unitOfWork.RollbackChanges();
				unitOfWork.CommitAndRefreshChanges();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="agente"></param>
		public void ChangeAgente(Agente agente)
		{
			if (agente == null)
				throw new ArgumentNullException("agente");
			IUnitOfWork unitOfWork = _agenteRepository.StoreContext;
			_agenteRepository.Modify(agente);
			try
			{
				unitOfWork.Commit();
			}
			catch (OptimisticConcurrencyException)
			{
				unitOfWork.RollbackChanges();
				unitOfWork.CommitAndRefreshChanges();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="agente"></param>
		public void DeleteAgente(Agente agente)
		{
			if (agente == null)
				throw new ArgumentNullException("agente");
			IUnitOfWork unitOfWork = _agenteRepository.StoreContext;
			_agenteRepository.Remove(agente);
			try
			{
				unitOfWork.Commit();
			}
			catch (OptimisticConcurrencyException)
			{
				unitOfWork.RollbackChanges();
				unitOfWork.CommitAndRefreshChanges();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="agente"></param>
		/// <param name="oldPassword"></param>
		/// <param name="newPassword"></param>
		public void ChangePassword(Agente agente, string oldPassword, string newPassword)
		{
			if (agente == null)
				throw new ArgumentNullException("agente");
			IUnitOfWork unitOfWork = _agenteRepository.StoreContext;
			string oldPasswordHash = SaltAndHash.Hash(oldPassword, agente.PasswordSalt);
			bool passwordsMatch = SaltAndHash.HashCompare(oldPasswordHash, agente.PasswordHash);
			if (!passwordsMatch)
				throw new InvalidOperationException("The passwords doesn't match!");
			string saltKey = Guid.NewGuid().ToString();
			string newPasswordSalt = SaltAndHash.Hash(newPassword, saltKey);
			string newPasswordHash = SaltAndHash.Hash(newPassword, newPasswordSalt);
			agente.PasswordHash = newPasswordHash;
			agente.PasswordSalt = newPasswordSalt;
			_agenteRepository.Modify(agente);
			try
			{
				unitOfWork.Commit();
			}
			catch (OptimisticConcurrencyException)
			{
				unitOfWork.RollbackChanges();
				unitOfWork.CommitAndRefreshChanges();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="userName"></param>
		/// <returns></returns>
		public Agente FindAgenteByUsuario(string userName)
		{
			if (string.IsNullOrWhiteSpace(userName) || string.IsNullOrEmpty(userName))
				throw new InvalidOperationException(Messages.exception_InvalidArgument);
			AgenteByUsuarioSpecification agenteByUsuarioSpecification = new AgenteByUsuarioSpecification(userName);
			return _agenteRepository.FindAgenteByUsuarioSpecification(agenteByUsuarioSpecification);
		}

		#endregion

		#region IDisposable Members

		public void Dispose()
		{
			if (_agenteRepository != null &&
				_agenteRepository.StoreContext != null)
				_agenteRepository.StoreContext.Dispose();
		}

		#endregion
	}
}
